/*
 *
 * System2 Simulation Framework
 * Copyright (C) 2011 - 2012 Jacob Dawid
 * jacob.dawid@googlemail.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "unit.h"
#include <math.h>

namespace System2 {
    Unit::Unit(PrimaryUnit primaryUnit, Prefix prefix) {
        if(primaryUnit != Without) {
            m_prefix = prefix;
            m_primaryUnit = primaryUnit;
        } else {
            m_prefix = None;
            m_primaryUnit = Without;
        }
    }

    Unit::Unit(const Unit& other) {
        m_prefix = other.m_prefix;
        m_primaryUnit = other.m_primaryUnit;
    }

    bool Unit::isCompatibleWith(const Unit &unit) const {
        if(m_primaryUnit == unit.m_primaryUnit)
           return true;
        return false;
    }

    Number Unit::adapt(const Unit& unit) {
        if(isCompatibleWith(unit)) {
            Number factor = prefixValue() / unit.prefixValue();
            m_prefix = unit.m_prefix;
            return factor;
        }
        return Number(1.0);
    }

    Unit::PrimaryUnit Unit::primaryUnit() const {
        return m_primaryUnit;
    }

    Unit::Prefix Unit::prefix() const {
        return m_prefix;
    }

    Number Unit::prefixValue() const {
        return m_prefixValues.value(m_prefix, Number(1.0));
    }

    QString Unit::toString() {
        return Unit::m_prefixes.value(m_prefix, "")
             + Unit::m_primaryUnits.value(m_primaryUnit, "");
    }

    QMap<Unit::Prefix, QString> Unit::initializePrefixes() {
        QMap<Unit::Prefix, QString> prefixes;
        prefixes[Yocto]     = "y";
        prefixes[Zepto]     = "z";
        prefixes[Atto]      = "a";
        prefixes[Femto]     = "f";
        prefixes[Pico]      = "p";
        prefixes[Nano]      = "n";
        prefixes[Micro]     = "μ";
        prefixes[Milli]     = "m";
        prefixes[Centi]     = "c";
        prefixes[Deci]      = "d";
        prefixes[None]      = "";
        prefixes[Deca]      = "da";
        prefixes[Hecto]     = "h";
        prefixes[Kilo]      = "k";
        prefixes[Mega]      = "M";
        prefixes[Giga]      = "G";
        prefixes[Tera]      = "T";
        prefixes[Peta]      = "P";
        prefixes[Exa]       = "E";
        prefixes[Zetta]     = "Z";
        prefixes[Yotta]     = "Y";

        prefixes[Yobi]      = "Yi";
        prefixes[Zebi]      = "Zi";
        prefixes[Exbi]      = "Ei";
        prefixes[Pebi]      = "Pi";
        prefixes[Tebi]      = "Ti";
        prefixes[Gibi]      = "Gi";
        prefixes[Mebi]      = "Mi";
        prefixes[Kibi]      = "Ki";

        return prefixes;
    }

    QMap<Unit::Prefix, Number> Unit::initializePrefixValues() {
        QMap<Unit::Prefix, Number> prefixValues;
        prefixValues[Yocto]     = Number(1e-24);
        prefixValues[Zepto]     = Number(1e-21);
        prefixValues[Atto]      = Number(1e-18);
        prefixValues[Femto]     = Number(1e-15);
        prefixValues[Pico]      = Number(1e-12);
        prefixValues[Nano]      = Number(1e-09);
        prefixValues[Micro]     = Number(1e-06);
        prefixValues[Milli]     = Number(1e-03);
        prefixValues[Centi]     = Number(1e-02);
        prefixValues[Deci]      = Number(1e-01);
        prefixValues[None]      = Number(1.0);
        prefixValues[Deca]      = Number(1e+01);
        prefixValues[Hecto]     = Number(1e+02);
        prefixValues[Kilo]      = Number(1e+03);
        prefixValues[Mega]      = Number(1e+06);
        prefixValues[Giga]      = Number(1e+09);
        prefixValues[Tera]      = Number(1e+12);
        prefixValues[Peta]      = Number(1e+15);
        prefixValues[Exa]       = Number(1e+18);
        prefixValues[Zetta]     = Number(1e+21);
        prefixValues[Yotta]     = Number(1e+24);

        prefixValues[Yobi]      = Number(pow(1024, 8));
        prefixValues[Zebi]      = Number(pow(1024, 7));
        prefixValues[Exbi]      = Number(pow(1024, 6));
        prefixValues[Pebi]      = Number(pow(1024, 5));
        prefixValues[Tebi]      = Number(pow(1024, 4));
        prefixValues[Gibi]      = Number(pow(1024, 3));
        prefixValues[Mebi]      = Number(pow(1024, 2));
        prefixValues[Kibi]      = Number(pow(1024, 1));

        return prefixValues;
    }

    QMap<Unit::PrimaryUnit, QString> Unit::initializePrimaryUnits() {
        QMap<Unit::PrimaryUnit, QString> primaryUnits;
        primaryUnits[Volt]      = "V";
        primaryUnits[Ampere]    = "A";
        primaryUnits[Ohm]       = "Ω";
        primaryUnits[Hertz]     = "Hz";
        primaryUnits[Seconds]   = "s";
        primaryUnits[Bit]       = "bit";
        primaryUnits[Byte]      = "B";
        primaryUnits[Without]   = "";
        return primaryUnits;
    }

    QMap<Unit::Prefix, QString> Unit::m_prefixes = Unit::initializePrefixes();
    QMap<Unit::Prefix, Number> Unit::m_prefixValues = Unit::initializePrefixValues();
    QMap<Unit::PrimaryUnit, QString> Unit:: m_primaryUnits = Unit::initializePrimaryUnits();
}
